from enum import Enum
import json
from collections import defaultdict

import sys
import os

sys.path.append(os.path.join(os.path.dirname(__file__), "../XnccCommon"))

import Common


class XnccLogLevel(Enum):
    FATAL = 1
    ERROR = 2
    WARN = 3
    INFO = 4
    DEBUG = 5


class ValidIpcQueueInfo:
    def __init__(self):
        self.Id: int = -1
        self.Size: int = -1
        self.SubTid: int = -1
        self.PubTid: int = -1


class ValidBusiQueueInfo:
    def __init__(self):
        self.Id: int = -1
        self.Size: int = -1
        self.SubTid: int = -1
        self.PubTid: int = -1
        self.MsgCodes: list[int] = []


class PlatformLogger:
    def __init__(self, d: dict):
        self.logPath = d.get("LogPath", "./")
        self.logName = d.get("LogName", "platlog")
        self.logLevel = d.get("LogLevel", XnccLogLevel.DEBUG)
        self.rotateSize = d.get("RotateSize", 512)

    def __str__(self):
        return (
            "{"
            + self.logPath
            + ","
            + self.logName
            + ","
            + str(self.logLevel)
            + ","
            + str(self.rotateSize)
            + "}"
        )


class PubInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("QueueId", -1)
        self.msgCodes: [] = d.get("MsgCodes", [])

    def __str__(self):
        return "{" + str(self.id) + "," + str(self.msgCodes) + "}"

    def __repr__(self):
        return self.__str__()


class QueueInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("Id", -1)
        self.size: int = d.get("Size", -1)

    def __str__(self):
        return "{" + str(self.id) + "," + str(self.size) + "}"

    def __repr__(self):
        return self.__str__()


class BusiThreadInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("Id", -1)
        self.name: str = d.get("Name", "")
        self.subIds: [] = d.get("Sub", [])
        self.pubIds: list[PubInfo] = []
        for ele in d.get("Pub", []):
            self.pubIds.append(PubInfo(ele))

    def __str__(self):
        return (
            "{"
            + str(self.id)
            + ","
            + self.name
            + ","
            + str(self.subIds)
            + ","
            + str(self.pubIds)
            + "}"
        )

    def __repr__(self):
        return self.__str__()


class SubServerThreadInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("Id", -1)
        self.name: str = d.get("Name", "")
        self.pubId: int = d.get("PubId", -1)

    def __str__(self):
        return "{" + str(self.id) + "," + self.name + "," + str(self.pubId) + "}"

    def __repr__(self):
        return self.__str__()


class ServerThreadInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("Id", -1)
        self.name: str = d.get("Name", "")
        self.pubId: int = d.get("PubId", -1)
        self.addr: str = d.get("Addr", "")
        self.subServers: list[SubServerThreadInfo] = []
        for ele in d.get("SubServerThreads", []):
            self.subServers.append(SubServerThreadInfo(ele))

    def __str__(self):
        return (
            "{"
            + str(self.id)
            + ","
            + self.name
            + ","
            + str(self.pubId)
            + ","
            + self.addr
            + ","
            + str(self.subServers)
            + "}"
        )

    def __repr__(self):
        return self.__str__()


class ClientThreadInfo:
    def __init__(self, d: dict):
        self.id: int = d.get("Id", -1)
        self.name: str = d.get("Name", "")
        self.pubId: int = d.get("PubId", -1)
        self.addr: str = d.get("Addr", "")
        self.reConn = False
        self.reConnMs: int = d.get("ReConnIntervalMs", -1)
        if self.reConnMs > 0:
            self.reConn = True

    def __str__(self):
        return (
            "{"
            + str(self.id)
            + ","
            + self.name
            + ","
            + str(self.pubId)
            + ","
            + self.addr
            + ","
            + str(self.reConn)
            + ","
            + str(self.reConnMs)
            + "}"
        )

    def __repr__(self):
        return self.__str__()


class RealQueueInfo:
    def __init__(self):
        self.CfgQueue: QueueInfo = None
        self.SubTids: list[int] = []
        self.PubTids: list[int] = []
        self.MsgCodes: list[int] = []
        self.IsIpcThreadPub = False


class PlatformConfig:
    def __init__(self, d: dict):
        self.platlog = PlatformLogger(d.get("PlatformLog", {}))
        self.queueList: list[QueueInfo] = []
        self.busiThreads: list[BusiThreadInfo] = []
        self.serverThreads: list[ServerThreadInfo] = []
        self.clientThreads: list[ClientThreadInfo] = []
        self.configQueueIds = set()
        self.RealingQueues: dict[int, RealQueueInfo] = defaultdict(RealQueueInfo)
        self.IpcPubQueues: dict[int, list[ValidIpcQueueInfo]] = defaultdict(
            list[ValidIpcQueueInfo]
        )
        self.BusiPubQueues: dict[int, list[ValidBusiQueueInfo]] = defaultdict(
            list[ValidBusiQueueInfo]
        )

        self.__fromDict(d)
        self.__checkConf()
        self.__genRealQueue()
        self.__classifyQueues()

    def __classifyQueues(self):
        for queue in self.RealingQueues.values():
            if queue.IsIpcThreadPub:
                for sub in queue.SubTids:
                    tmpqueue = ValidIpcQueueInfo()
                    tmpqueue.Id = queue.CfgQueue.id
                    tmpqueue.Size = queue.CfgQueue.size
                    tmpqueue.SubTid = sub
                    tmpqueue.PubTid = queue.PubTids[0]
                    self.IpcPubQueues[tmpqueue.PubTid].append(tmpqueue)
            else:
                for subid in queue.SubTids:
                    tmpqueue = ValidBusiQueueInfo()
                    tmpqueue.Id = queue.CfgQueue.id
                    tmpqueue.Size = queue.CfgQueue.size
                    tmpqueue.SubTid = sub
                    tmpqueue.PubTid = queue.PubTids[0]
                    self.BusiPubQueues[tmpqueue.PubTid].append(tmpqueue)

    def __genRealQueue(self):
        for cfgQueue in self.queueList:
            self.RealingQueues[cfgQueue.id].CfgQueue = cfgQueue
        for busi in self.busiThreads:
            for subid in busi.subIds:
                self.RealingQueues[subid].SubTids.append(busi.id)
            for pub in busi.pubIds:
                self.RealingQueues[pub.id].PubTids.append(busi.id)
                self.RealingQueues[pub.id].MsgCodes = pub.msgCodes
                self.RealingQueues[pub.id].IsIpcThreadPub = False
        for server in self.serverThreads:
            self.RealingQueues[server.pubId].PubTids.append(server.id)
            self.RealingQueues[server.pubId].IsIpcThreadPub = True
            for subserver in server.subServers:
                self.RealingQueues[subserver.pubId].PubTids.append(subserver.id)
                self.RealingQueues[subserver.pubId].IsIpcThreadPub = True
        for client in self.clientThreads:
            self.RealingQueues[client.pubId].PubTids.append(client.id)
            self.RealingQueues[client.pubId].IsIpcThreadPub = True
        for cfgQueue in self.queueList:
            if (
                len(self.RealingQueues[cfgQueue.id].SubTids) == 0
                or len(self.RealingQueues[cfgQueue.id].PubTids) == 0
            ):
                del self.RealingQueues[cfgQueue.id]
                print(
                    "RingQueue = {}, subscriber and publisher are not all exisit, will not gen real RingQueue".format(
                        cfgQueue.id
                    )
                )

    def __fromDict(self, d: dict):
        for ele in d.get("Queues", []):
            self.queueList.append(QueueInfo(ele))
        for ele in d.get("BusinessThreads", []):
            self.busiThreads.append(BusiThreadInfo(ele))
        for ele in d.get("ServerThreads", []):
            self.serverThreads.append(ServerThreadInfo(ele))
        for ele in d.get("ClientThreads", []):
            self.clientThreads.append(ClientThreadInfo(ele))

    def __checkConf(self):
        self.__checkQueueList()
        self.__checkThreadId()
        self.__checkBusiThread()
        self.__checkServerThread()
        self.__checkClientThread()
        self.__checkQueueRace()

    def __hasRepeatedId(self, idList: []):
        if len(set(idList)) != len(idList):
            return True
        else:
            return False

    def __hasUnDefinedId(self, idList: []):
        for id in idList:
            if id not in self.configQueueIds:
                return True
        return False

    def __checkQueueRace(self):
        allPubIds: dict[int, list[int]] = defaultdict(list)
        for busi in self.busiThreads:
            for ele in busi.pubIds:
                allPubIds[ele.id].append(busi.id)
        for server in self.serverThreads:
            allPubIds[server.pubId].append(server.id)
            for subserver in server.subServers:
                allPubIds[subserver.pubId].append(subserver.id)
        for client in self.clientThreads:
            allPubIds[client.pubId].append(client.id)
        for pubid, threadids in allPubIds.items():
            if len(threadids) > 1:
                raise Exception(
                    "not allowed multi threads "
                    + str(threadids)
                    + " pub msg to one queueid "
                    + str(pubid)
                )

    def __checkClientThread(self):
        for client in self.clientThreads:
            if client.pubId <= 0:
                raise Exception(
                    "client thread ",
                    client.id,
                    "pubid must > 0",
                )
            if self.__hasUnDefinedId([client.pubId]):
                raise Exception(
                    "client thread ",
                    client.id,
                    "pubid ",
                    client.pubId,
                    " not defined in queue list",
                )

    def __checkServerThread(self):
        for server in self.serverThreads:
            if server.pubId <= 0:
                raise Exception(
                    "server thread ",
                    server.id,
                    "pubid must > 0",
                )
            if self.__hasUnDefinedId([server.pubId]):
                raise Exception(
                    "server thread ",
                    server.id,
                    "pubid ",
                    server.pubId,
                    " not defined in queue list",
                )
            for subserver in server.subServers:
                if subserver.pubId <= 0:
                    raise Exception(
                        "subserver thread ",
                        subserver.id,
                        "pubid must > 0",
                    )
                if self.__hasUnDefinedId([subserver.pubId]):
                    raise Exception(
                        "subserver thread ",
                        subserver.id,
                        "pubid ",
                        subserver.pubId,
                        " not defined in queue list",
                    )

    def __checkBusiThread(self):
        for busi in self.busiThreads:
            if self.__hasUnDefinedId(busi.subIds):
                raise Exception(
                    "busi thread id ",
                    busi.id,
                    " subids has not defined ids in queuelist",
                )
            if self.__hasRepeatedId(busi.subIds):
                raise Exception(
                    "busi thread id ",
                    busi.id,
                    " subids has repeated id",
                )
            tmpPubIds = []
            for ele in busi.pubIds:
                tmpPubIds.append(ele.id)
                if self.__hasRepeatedId(ele.msgCodes):
                    raise Exception(
                        "busi thread id ",
                        busi.id,
                        " pudid ",
                        ele.id,
                        " has repeated msg code",
                    )
                for msgcode in ele.msgCodes:
                    if msgcode < Common.MAX_INNER_MSG_CODE:
                        raise Exception(
                            "busi thread id ",
                            busi.id,
                            " pudid ",
                            ele.id,
                            " msg code error, must < ",
                            Common.MAX_INNER_MSG_CODE,
                        )
            if self.__hasUnDefinedId(tmpPubIds):
                raise Exception(
                    "busi thread id ",
                    busi.id,
                    " pudids has not defined ids in queuelist",
                )
            if self.__hasRepeatedId(tmpPubIds):
                raise Exception(
                    "busi thread id ",
                    busi.id,
                    " pudids has repeated id",
                )
            if len(set(tmpPubIds) & set(busi.subIds)) > 0:
                raise Exception(
                    "busi thread id ",
                    busi.id,
                    " self pub and sub self",
                )

    def __checkQueueList(self):
        idList = []
        for queue in self.queueList:
            if queue.id <= 0:
                raise Exception(" id must > 0")
            idList.append(queue.id)
        if self.__hasRepeatedId(idList):
            raise Exception("queue list has repeated id")
        self.configQueueIds = set(idList)

    def __checkThreadId(self):
        idList = []
        for ele in self.busiThreads:
            if ele.id <= 0:
                raise Exception("busi thread id must > 0")
            idList.append(ele.id)
        for ele in self.clientThreads:
            if ele.id <= 0:
                raise Exception("client id must > 0")
            idList.append(ele.id)
        for ele in self.serverThreads:
            if ele.id <= 0:
                raise Exception("server id must > 0")
            idList.append(ele.id)
            for subele in ele.subServers:
                if subele.id <= 0:
                    raise Exception("sub server id must > 0")
                idList.append(subele.id)
        if self.__hasRepeatedId(idList):
            raise Exception("threads ids have repeated id")

    def __str__(self):
        return (
            "{"
            + str(self.platlog)
            + ","
            + str(self.queueList)
            + ","
            + str(self.busiThreads)
            + ","
            + str(self.serverThreads)
            + ","
            + str(self.clientThreads)
            + "}"
        )


with open("conf.json", "r") as jsonfile:
    result = json.load(jsonfile)
    platConfig = PlatformConfig(result)
    print(platConfig)
